﻿// Xshell Execution Condition Interface
// 
// <copyright file="XsCondition.cs" company="TaskyMedia LLC">
//   Copyright © 2010 TaskyMedia LLC
// </copyright>

namespace Xshell
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;

    /// <summary>
    /// Represents a condition on which to base execution.
    /// </summary>
    public abstract class XsCondition
    {
        /// <summary>
        /// A cached list of reflected <see cref="XsCondition"/> implementations.
        /// </summary>
        private static Dictionary<string, Type> _conditionLookupTable = new Dictionary<string, Type>();

        /// <summary>
        /// Object used to synchronize changes to the Command types lookup table.
        /// </summary>
        private static object _tableLockObject = new object();

        /// <summary>
        /// Gets a value indicating whether the condition has been met.
        /// </summary>
        /// <value><c>true</c> if this condition has been met; otherwise, <c>false</c>.</value>
        public abstract bool IsValid { get; }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>The value.</value>
        public string Value { get; set; }

        /// <summary>
        /// Creates a new <see cref="XsCondition"/> instance of the correct type from a string.
        /// </summary>
        /// <param name="metadataCache">The metadata cache.</param>
        /// <param name="conditionName">Name of the condition.</param>
        /// <param name="conditionValue">The condition value.</param>
        /// <returns>
        /// Returns a new <see cref="XsCondition"/> instance of the correct type from a string.
        /// </returns>
        public static XsCondition CreateFromString(IMetadataCache metadataCache, string conditionName, string conditionValue)
        {
            XsCondition result = null;

            if (_conditionLookupTable.Count == 0)
            {
                BuildConditionLookupTable(metadataCache);
            }

            Type conditionType = null;
            if (_conditionLookupTable.TryGetValue(conditionName.ToLowerInvariant(), out conditionType))
            {
                result = (XsCondition)Activator.CreateInstance(conditionType);
                result.Value = conditionValue;
            }

            return result;
        }

        /// <summary>
        /// Builds the condition types lookup table.
        /// </summary>
        /// <param name="metadataCache">The metadata cache.</param>
        private static void BuildConditionLookupTable(IMetadataCache metadataCache)
        {
            lock (_tableLockObject)
            {
                if (_conditionLookupTable.Count == 0)
                {
                    // Build a list of command types
                    foreach (Type type in metadataCache.GetCachedTypes())
                    {
                        if (!type.IsAbstract && typeof(XsCondition).IsAssignableFrom(type))
                        {
                            object[] attributes = type.GetCustomAttributes(typeof(XsConditionAttribute), true);
                            if (attributes != null)
                            {
                                XsConditionAttribute attribute = (XsConditionAttribute)attributes[0];
                                _conditionLookupTable.Add(attribute.Name.ToLowerInvariant(), type);
                            }
                        }
                    }
                }
            }
        }
    }
}
